#include "Indice.h"
#include "Contexto.h"
#include <iostream>
using namespace std;

Indice::Indice()
{
	for(int i = 0; i<256256; i++)
	{
		contextos[i]=0;
	}
}

Indice::~Indice()
{
	for(int i = 0; i<256256; i++)
	{
		if (contextos[i] != 0)
			delete contextos[i];
	}
}

int Indice::obtenerPosContexto(unsigned char car1,unsigned char car2)
{
	int posContexto = 0;
	int aux1 = 0;
	int aux2 = 0;
	aux1 = (int) car1;
	aux2 = (int) car2;
	aux1 = aux1*1000;
	posContexto = aux1 + aux2;
	if(posContexto<0 || posContexto> 256256)
		aux1=0;
	return posContexto;
}

Contexto* Indice::obtenerContexto(int posContexto)
{
	if (( contextos[posContexto] == NULL) || (contextos[posContexto]-> getSize() <=0) || contextos[posContexto] -> getSize() >4 || (contextos[posContexto] -> getPrimerPrediccion()->getPosicion() < 0) )
	{
		contextos[posContexto] = new Contexto();
	}
	return contextos[posContexto] ;
}
 
Compresion* Indice::generarCompresion(Compresion *compr, BloqueTexto &bloque)
{
	if (compr->getPosicionCaracterActual() >= bloque.getCantCaracteresValidos() - 1)
	{
		if(bloque.getCantCaracteresValidos() == bloque.getSize() && !bloque.leyomenos())
			bloque.setFinBloque(true);
		else
		{
			bloque.setFinArch(true);
			bloque.setFinBloque(true);
		}
		return compr;
	}
	int posCtx = obtenerPosContexto(compr->getContexto(0), compr->getContexto(1));
	Contexto *contexto = obtenerContexto(posCtx);
	int size = contexto->getSize();
	
	// Si la lista de predicciones en esa posición esta vacía, la agrego en la primer posición
	// con el puntaje inicial.
	if ( size == 0)
	{
		contexto->addPrediccion(compr->getPosicionCaracterActual());
		compr->setLongitud(0);
		return compr;
	}

	Prediccion* primerPred = contexto->getPrimerPrediccion();
	int posCaracterActual = compr->getPosicionCaracterActual();
	int posCaracterPrediccion = primerPred->getPosicion();
	char caracterPrediccion = bloque.getCaracterEnPos(primerPred->getPosicion());
	char caracterActual = bloque.getCaracterEnPos(posCaracterActual);

	int match = 0;
	while( caracterActual == caracterPrediccion && ((match + posCaracterPrediccion) < posCaracterActual) && match<255)
	{
		match ++;
		caracterActual = bloque.getCaracterEnPos(posCaracterActual + match);
		caracterPrediccion = bloque.getCaracterEnPos(posCaracterPrediccion + match);
		if (posCaracterActual + match >= bloque.getCantCaracteresValidos() - 1)
		{
			//si al avanzar la busqueda de match, no hay mas caracteres validos, seteo el estado del bloque segun corresponda
			if(bloque.getCantCaracteresValidos() == bloque.getSize() && !bloque.leyomenos())
				bloque.setFinBloque(true);
			else
			{
				bloque.setFinArch(true);
				bloque.setFinBloque(true);
			}
			caracterActual = bloque.getCaracterEnPos(posCaracterActual);
			compr->setLongitud(match);
			actualizarPuntajes(compr, caracterActual, posCaracterActual, contexto, bloque);
			return compr;
		}

	}
	caracterActual = bloque.getCaracterEnPos(posCaracterActual);
	compr->setLongitud(match);
	actualizarPuntajes(compr, caracterActual, posCaracterActual, contexto, bloque);
	return compr;
}

Compresion* Indice::generarDescompresion(Compresion *compr, BloqueTextoSalida &bloque)
{
	int posCtx = obtenerPosContexto(compr->getContexto(0), compr->getContexto(1));
	Contexto *contexto = obtenerContexto(posCtx);
	int size = contexto->getSize();
	int longitudMatch = compr->getLongitud();

	if (longitudMatch == 0 && size == 0)
		{
			contexto->addPrediccion(compr->getPosicionCaracterActual());
			compr->setLongitud(0);
			bloque.setCaracterEnPos(compr->getCaracter(), compr->getPosicionCaracterActual());
			if( compr->getPosicionCaracterActual() == bloque.getSize() - 1)
				{
					bloque.setFinBloque(true);
				}
			return compr;

		}

	Prediccion* primerPred = contexto->getPrimerPrediccion();
	int posCaracterActual = compr->getPosicionCaracterActual();
	int posCaracterPrediccion = primerPred->getPosicion();
	char caracterActual = compr->getCaracter();
	int aux = 0;
	while( aux < longitudMatch)
	{
		bloque.setCaracterEnPos(bloque.getCaracterEnPos(posCaracterPrediccion + aux), posCaracterActual + aux);
		aux++;
	}
	//if( !bloque.finArchivo())
	//{
		bloque.setCaracterEnPos(caracterActual, posCaracterActual + longitudMatch);
	//}
	if( posCaracterActual + longitudMatch >= bloque.getSize() - 1)
	{
		bloque.setFinBloque(true);
	}
	actualizarPuntajes(compr, caracterActual, posCaracterActual, contexto, bloque);
	return compr;
}

//Para comprimir (recibe un BloqueTexto)
void Indice::actualizarPuntajes(Compresion * compr, char caracterActual, int posCaracterActual, Contexto *contexto, BloqueTexto &bloque)
{
	//si no hubo match, resto puntaje a la primer predicción e intento agregar la nueva.
	//si la nueva predicción coincide con alguna otra le sumo puntaje de coincidencia (si el
	//puntaje es 0 la reemplazo por esta nueva).
	//Se controla que en una posible incorporación de una nueva predicción, esta no esté repetida.
	if(compr->getLongitud() == 0)
		{
			compr->setLongitud(0);
			contexto->sumarPuntaje(0,PUNTAJE_RESTA);
			bool valeCero = false;
			bool finEjec = false;
			int posRepetido = -1;
			int aux = 1;
			Prediccion* pred;
			while(aux < contexto->getSize() && !finEjec)
			{
				pred = contexto->getPrediccionEnPos(aux);
				if( pred->getPuntaje() == 0)
				{
					if( pred->getCaracter(bloque) != caracterActual && !valeCero && contexto->estaLLena())
					{
						valeCero = true;
						posRepetido = aux;
					} else
					{
						pred->setPosicion(posCaracterActual);
						pred->setPuntajeInicial();
						contexto->reordenar();
						valeCero = false;
						finEjec = true;
					}

				} else
				{
					if( pred->getCaracter(bloque) == bloque.getCaracterEnPos(posCaracterActual))
					{
						pred->setPosicion(posCaracterActual);
						pred->sumarPuntaje(PUNTAJE_SUMA);
						contexto->reordenar();
						valeCero = false;
						finEjec = true;
					}
				}
				aux++;
			}

			if(!contexto->estaLLena() && !finEjec)
			{
				finEjec =true;
				contexto->addPrediccion(compr->getPosicionCaracterActual());
				contexto->reordenar();
			}

			if(contexto->estaLLena() && !finEjec)
						{
							finEjec =true;
							contexto->reordenar();
						}

			if(valeCero)
			{
				pred = contexto->getPrediccionEnPos(posRepetido);
				pred->setPosicion(posCaracterActual);
				pred->setPuntajeInicial();
				contexto->reordenar();
			}
		}

		// Si hubo match sumo puntaje en la primer posición y le resto al resto. No es necesario
		// reordenar ya que el primer elemento seguirá siendo el de mayor puntaje.
		if( compr->getLongitud() != 0)
		{
			contexto->sumarPuntaje(0,PUNTAJE_SUMA);
			int aux = 1;
			Prediccion pred;
			while(aux < contexto->getSize())
			{
				contexto->getPrediccionEnPos(aux)->sumarPuntaje(PUNTAJE_RESTA);
				aux++;
			}
		}
}

//Para descomprimir (recibe BloqueTextoSalida)
void Indice::actualizarPuntajes(Compresion * compr, char caracterActual, int posCaracterActual, Contexto *contexto, BloqueTextoSalida &bloque)
{
	//si no hubo match, resto puntaje a la primer predicción e intento agregar la nueva.
	//si la nueva predicción coincide con alguna otra le sumo puntaje de coincidencia (si el
	//puntaje es 0 la reemplazo por esta nueva).
	//Se controla que en una posible incorporación de una nueva predicción, esta no esté repetida.
	if(compr->getLongitud() == 0)
			{
				compr->setLongitud(0);
				contexto->sumarPuntaje(0,PUNTAJE_RESTA);
				bool valeCero = false;
				bool finEjec = false;
				int posRepetido = -1;
				int aux = 1;
				Prediccion* pred;
				while(aux < contexto->getSize() && !finEjec)
				{
					pred = contexto->getPrediccionEnPos(aux);
					if( pred->getPuntaje() == 0)
					{
						if( pred->getCaracter(bloque) != caracterActual && !valeCero && contexto->estaLLena())
						{
							valeCero = true;
							posRepetido = aux;
						} else
						{
							pred->setPosicion(posCaracterActual);
							pred->setPuntajeInicial();
							contexto->reordenar();
							valeCero = false;
							finEjec = true;
						}

					} else
					{
						if( pred->getCaracter(bloque) == bloque.getCaracterEnPos(posCaracterActual))
						{
							pred->setPosicion(posCaracterActual);
							pred->sumarPuntaje(PUNTAJE_SUMA);
							contexto->reordenar();
							valeCero = false;
							finEjec = true;
						}
					}
					aux++;
				}

				if(!contexto->estaLLena() && !finEjec)
				{
					finEjec =true;
					contexto->addPrediccion(compr->getPosicionCaracterActual());
					contexto->reordenar();
				}

				if(contexto->estaLLena() && !finEjec)
							{
								finEjec =true;
								contexto->reordenar();
							}

				if(valeCero)
				{
					pred = contexto->getPrediccionEnPos(posRepetido);
					pred->setPosicion(posCaracterActual);
					pred->setPuntajeInicial();
					contexto->reordenar();
				}
			}

			// Si hubo match sumo puntaje en la primer posición y le resto al resto. No es necesario
			// reordenar ya que el primer elemento seguirá siendo el de mayor puntaje.
			if( compr->getLongitud() != 0)
			{
				contexto->sumarPuntaje(0,PUNTAJE_SUMA);
				int aux = 1;
				Prediccion pred;
				while(aux < contexto->getSize())
				{
					contexto->getPrediccionEnPos(aux)->sumarPuntaje(PUNTAJE_RESTA);
					aux++;
				}
			}

}

